package com.automapper.executor;

import java.sql.Connection;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import org.apache.ibatis.executor.BatchExecutor;
import org.apache.ibatis.executor.BatchResult;
import org.apache.ibatis.executor.statement.StatementHandler;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.reflection.MetaObject;
import org.apache.ibatis.reflection.SystemMetaObject;
import org.apache.ibatis.session.Configuration;
import org.apache.ibatis.session.RowBounds;
import org.apache.ibatis.transaction.Transaction;

import com.automapper.constants.ParameterConstants;
import com.automapper.utils.ParameterUtils;

/**
 * 在mybatis使用ExecutorType.SIMPLE模式时，将其转换ExecutorType.BATCH模式，实现批量入库操作
 * 注意：必须配合AutoMapperChangeExecutorTypeInterceptor插件才能使用。
 * 	        现在只执行update批量操作后，同一事物内的delete，insert操作都可以使用ExecutorType.BATCH模式，
 * 	        每条session创建都会创建一个新的Executor对象，Executor对象创建在DefaultSqlSessionFactory.openSessionFromDataSource方法内
 * @author K
 * 
 */
public class AutoMapperBatchExecutor extends BatchExecutor {
	
	private MetaObject metaObject;
	
	public AutoMapperBatchExecutor(Configuration configuration, Transaction transaction) {
		super(configuration, transaction);
		//MetaObject是Mybatis提供的一个的工具类,用于获取属性的值（支持OGNL的方法）
		metaObject = SystemMetaObject.forObject(this);
	}

	@Override
	public int doUpdate(MappedStatement ms, Object parameterObject) throws SQLException {
		// 不是通过AutoMapperRepository对象调用update语句，直接执行Mybatis原生BatchExecutor对的doUpdate方法
		if (!ParameterUtils.isAutoMapperParameter(parameterObject)) {
			return super.doUpdate(ms, parameterObject);
		}
		
		int i = 0;
		List<Statement> statementList = (List<Statement>) metaObject.getValue("statementList");
		List<BatchResult> batchResultList = (List<BatchResult>) metaObject.getValue("batchResultList");
		// 将传入的参数使用集合封装
		List<Object> parameterList = ParameterUtils.getParameterList((Map<String, Object>) parameterObject);
		Map<String, Object> map = new HashMap<>();
		map.put(ParameterConstants.PARAM, parameterList.get(i));
		StatementHandler handler = configuration.newStatementHandler(this, ms, map, RowBounds.DEFAULT, null, null);
		Connection connection = getConnection(ms.getStatementLog());
		// 在这里执行prepare方法，会触发拦截器
		// AutoMapperInterceptor插件会在这里拼装sql语句，并赋值到该对象里
		Statement stmt = handler.prepare(connection);
		statementList.add(stmt);
		// 设置参数
		handler.parameterize(stmt);
		handler.batch(stmt);
		
		BoundSql boundSql = handler.getBoundSql();
		String sql = boundSql.getSql();
        batchResultList.add(new BatchResult(ms, sql, map));
		i++;
		
		for (; i < parameterList.size(); i ++) {
			map = new HashMap<>();
			map.put(ParameterConstants.PARAM, parameterList.get(i));
			// 这里为什么要传boundSql参数，上面的创建handler不传boundSql参数，是因为没有使用handler.prepare方法，
			// 触发不了拦截器，导致不能为boundSql里的List<ParameterMapping>初始化，
			// List<ParameterMapping>会在handler.parameterize方法里为Statement设置入库的参数
			handler = configuration.newStatementHandler(this, ms, map, RowBounds.DEFAULT, null, boundSql);
			BatchResult batchResult = batchResultList.get(statementList.size() - 1);
			batchResult.addParameterObject(map);
			
			handler.parameterize(stmt);
			handler.batch(stmt);
		}
		return BATCH_UPDATE_RETURN_VALUE;
	}
}
